#Copyright Ken Faulkner 2007.
import sys
import wx
from wx.lib.mixins.listctrl import CheckListCtrlMixin

from Common.logRoutine import *
import traceback
from Framework.Downloader.ThreadedLinkDownloader import ThreadedLinkDownloader
from Framework.Downloader.RealAudioDownloader import RealAudioDownloader
import Queue
from Datatypes.AudioDetails import AudioDetails
from Framework.CacheManager.CacheManager import CacheManager
from configobj import ConfigObj
from configui import ConfigDialog
from Framework.Downloader.Commands import *
import os
import os.path
from Framework.GUI.ListPanel import ListPanel

class MainPanel( wx.Panel ):
  """
  Creation of downloaders threads from this class is JUST WRONG.
  Not sure what I was on... but it was good stuff!
  
  """
  
  def __init__(self, parent ):
    wx.Panel.__init__(self, parent, -1)

    
    # log file.
    self.log = getLogger()
    
    try:


      # set the font.
      font = wx.Font(13, wx.SWISS, wx.NORMAL, wx.NORMAL)
      self.SetFont( font )

      # description
      txtCtrl = wx.TextCtrl( self, -1, size=(200,200), style = wx.TE_READONLY |  wx.TE_MULTILINE )
 
      # panel for list of titles.
      self.list_panel = ListPanel( self, txtCtrl )
 
      # sizers
      vSizer = wx.BoxSizer( wx.VERTICAL )
      hSizer = wx.BoxSizer( wx.HORIZONTAL )
   
      # add the crap.
      vSizer.Add( hSizer, 4 , wx.EXPAND)

      ctrlPanel = self.create_control_panel( )
      vSizer.Add( ctrlPanel, 1 , wx.EXPAND)
  
      hSizer.Add( self.list_panel, 1, wx.EXPAND)
      hSizer.Add( txtCtrl, 1, wx.EXPAND)
  
      self.SetSizer( vSizer)


      # setup a timer.
      self.timer = wx.PyTimer( self.on_timer )
      self.timer.Start( 1000 )
      
      # items to be displayed to the user.
      self.item_list = []
      self.saved_search_terms = []
      self.search_term = ""

      # Q's for talking to downloader threads.
      # requesting items for download and responses on status's's's's
      self.requestQ = Queue.Queue()
      self.responseQ = Queue.Queue()
     
      # various config files.
      # self.config for global config.
      # self.userConfig for user specific info.
      self.home_dir = os.path.join( os.getenv("HOME"), ".bbc") 

      self.config = ConfigObj( os.path.join( self.home_dir, "bbc.cfg") )
      self.user_config = ConfigObj( os.path.join( self.home_dir, "user.cfg") )
        
      # Cache for data!!
      # This is to store what's been downloaded etc.
      self.cache_manager = CacheManager( self.config )
        
      # link downloader
      self.threaded_link_downloader = ThreadedLinkDownloader( self.config )

      # dictionary of tuples
      # ( thread, command Q )
      # key is the thread no.
      self.thread_and_commandQ = {}

      # base dir...  where the binary sits.
      self.base_dir = os.getcwd() 

    except:
      self.log.error("MainPanel::__init__ ex " + traceback.format_exc() )

  def on_check_item(self, index, flag):
    """
    Gets called by CheckListCtrl when item selected.
    """
    self.log.info("MainPanel::on_check_item start") 
    
    try:
    
      self.item_list[ index ].checked_for_download = flag
    
    except:
      self.log.error("ListPanel::on_check_item ex " + traceback.format_exc() )
  
  def issue_command( self, command, item ):
    """
    Send a command to the downloader thread for a particular item.
    """
    
    self.log.info("MainPanel::issue_command start")
    try:
    
      # this is the thread for the item in question
      thread_no_str = item.thread_no
      
      self.log.debug("item is on thread " + str( thread_no_str ) )
      
      if thread_no_str != None:
        
        tq = self.thread_and_commandQ.get( thread_no_str, None )
        
        self.log.debug("tq for item is " + str( tq ) )
        
        if tq != None:
          t, q = tq
          q.put( command )
        else:
          self.log.error("issue_command to non-existent thread " + thread_no_str )
          
    except:
      self.log.error("MainPanel::issue_command ex " + traceback.format_exc() )
    
    
  def on_timer( self ):
    """
    On Timer...   check download status
    """
    #self.log.info("MainPanel::on_timer start")
    
    try:
      # check responseQ and see if any downloads and a finish.
      
      
      try:
        
        # check if we have fresh contents from the link downloader.
        if self.threaded_link_downloader.have_new_results:
          
          self.item_list = self.threaded_link_downloader.get_latest_links()
          
          self.search_list()
          self.list_panel.populate_list(  )
          
      except:
        self.log.error("MainPanel::on_timer link downloader ex " + traceback.format_exc() )
      
      
      # check if we have any responses and thus any status updates?      
      try:
      
        item = self.responseQ.get_nowait()

        self.log.debug("timer item " + str( item ) )
        
        # if we have download complete then save in cache.
        if item.status == AudioDetails.STATUS_COMPLETELY_FINISHED: 
          item.checked_for_download = False
          
          # have a response, so store.
          self.cache_manager.saveAllAudio( self.item_list)
                  
        self.update_single_item( item )
        
      except Queue.Empty , e:
        # if empty, just skip it.
        # dont bother logging... will log WAY too much.
        pass
      
    except:
      self.log.error("MainPanel::on_timer ex " + traceback.format_exc() )
    
  def create_download_threads( self ):
    """
    Create threads to do the downloading.
    """
    self.log.info("MainPanel::create_download_threads start")
    try:
  
      # setup download.
      if self.thread_and_commandQ == {}:
      
        no_threads = int( self.user_config['NoDownloadThreads'] )
        
        for i in xrange( no_threads ):
        
          q = Queue.Queue()
          t = RealAudioDownloader( self.config, self.user_config, self.requestQ ,self.responseQ, q )
          
          # for later identification.
          
          thread_no_str = str( i )
          t.setName( thread_no_str )
          self.thread_and_commandQ[ thread_no_str ] = (t, q )
          
        for t, q in self.thread_and_commandQ.itervalues():
          self.log.debug("thread " + str( t ) )
          
          t.start()

      self.log.debug("thread_and_commandQ " + str( self.thread_and_commandQ) )
      
    except:
      self.log.error("MainPanel::create_download_threads ex " + traceback.format_exc() )
    
    
  def create_control_panel( self):
    """
    Populate the control panel with appropriate buttons etc
    """
    self.log.info("MainPanel::create_control_panel start")
    vSizer = wx.BoxSizer( wx.VERTICAL )

    try:


      hSizer = wx.BoxSizer( wx.HORIZONTAL )
      refresh_image = wx.Bitmap("images/refresh.png", wx.BITMAP_TYPE_PNG)
      id = wx.NewId()
      refresh_button = wx.BitmapButton( self, id, refresh_image )
      refresh_button.SetToolTipString("Refresh list")
      self.Bind( wx.EVT_BUTTON, self.update_list_button, refresh_button)
      hSizer.Add( refresh_button )

      download_image = wx.Bitmap("images/download.png", wx.BITMAP_TYPE_PNG)
      id = wx.NewId()
      download_button = wx.BitmapButton( self, id, download_image )
      download_button.SetToolTipString("Download selected streams")
      self.Bind( wx.EVT_BUTTON, self.start_download, download_button)
      hSizer.Add( download_button )

      cancel_download_image = wx.Bitmap("images/canceldownload.png", wx.BITMAP_TYPE_PNG)
      id = wx.NewId()
      cancel_download_button = wx.BitmapButton( self, id, cancel_download_image )
      cancel_download_button.SetToolTipString("Cancel Downloads")
      self.Bind( wx.EVT_BUTTON, self.cancel_downloads, cancel_download_button)
      hSizer.Add( cancel_download_button )
      
      config_image = wx.Bitmap("images/config.png", wx.BITMAP_TYPE_PNG)
      id = wx.NewId()
      config_button = wx.BitmapButton( self, id, config_image )
      config_button.SetToolTipString("Configure")
      
      self.Bind( wx.EVT_BUTTON, self.start_config, config_button)
      hSizer.Add( config_button )
      
      vSizer.Add( hSizer )

      # search bar.
      textHSizer = wx.BoxSizer( wx.HORIZONTAL )
      staticSearch = wx.StaticText( self, -1, "Search ")
      textHSizer.Add( staticSearch )

      search_text = wx.TextCtrl( self, -1, "", size=(200,-1), style=wx.TE_PROCESS_ENTER )

      self.Bind( wx.EVT_TEXT_ENTER, self.search_results, search_text )

      textHSizer.Add( search_text )
      vSizer.Add( textHSizer )
    except:
      self.log.error("MainPanel::create_control_panel ex " + traceback.format_exc() )

    return vSizer

  def search_results( self, evt ):
    self.log.info("MainPanel::search_results start")

    term = evt.GetString()

    self.search_term = term
     
    self.search_and_display()

  def update_single_item( self, item ):
    """
    update single item
    """
    self.log.info("MainPanel::update_single_item start")
    try:
      
      self.list_panel.update_single_item( item )
      
    except:
      self.log.error("MainPanel::update_single_item ex " + traceback.format_exc() )
      
      
    
  def search_and_display( self ):
    """
    search the list and update the display
    """
    self.log.info("MainPanel::search_and_display start")
    try:
      self.search_list()
      self.list_panel.populate_list(  )
      
    except:
      self.log.error("MainPanel::search_and_display ex " + traceback.format_exc() )
    
  def search_list( self ):
    """
    Marks any of the results as "special"
    ie, matches search criteria
    
    """
    self.log.info("MainPanel::search_list start")
    try:

      if self.search_term != "":
        
        search_terms = [ self.search_term ]
      
      else:
 
        # always load from disk.
        config = ConfigObj( self.base_dir + "/Config/user.cfg")
        self.log.debug("config " + str( config ) )
        search_terms = config['SearchTerms']
      
      # go through all items.
      for i in self.item_list:

        # mark all entries as not found.
        if i.status == AudioDetails.STATUS_FOUND_IN_SEARCH or i.status == AudioDetails.STATUS_NORMAL: 
          i.status = AudioDetails.STATUS_NORMAL
   
        for search_term in search_terms:
        
          search_term = search_term.lower()
          
          if search_term != "":
            title = i.title.lower()
            
            idx = title.find( search_term )
            if idx != -1:
              i.status = AudioDetails.STATUS_FOUND_IN_SEARCH
      
            else:
              desc = i.description
              idx = desc.find( search_term )
              if idx != -1:
                i.status = AudioDetails.STATUS_FOUND_IN_SEARCH
      
    except:
      self.log.error("MainPanel::search_list ex " + traceback.format_exc() )




  def start_config(self, event):
    """
    Start the config
    """

    self.log.info("MainPanel::start_config start")
    try:
      dia = ConfigDialog( self, -1, "Config", size=(800,600))
      
      val = dia.ShowModal()
      
      if val == wx.ID_OK:
        self.log.debug("config ok")
        
        search_terms = []
        
        for i in dia.nb.searchFields:
          search_terms.append( i.GetValue() )

        # same search terms.
        self.user_config['SearchTerms'] = search_terms
        self.user_config['EncodingLevel'] = dia.nb.encodingLevel  
        self.user_config['DownloadDir'] = dia.nb.downloadDir.GetValue()
        self.user_config['NoDownloadThreads'] = dia.nb.maxThreads.GetValue()
        self.user_config['MPlayerBinary'] = dia.nb.mplayerBinary.GetValue()
        self.user_config['LameBinary'] = dia.nb.lameBinary.GetValue()
        
        self.user_config.write()
          
      else:
        self.log.debug("config not ok")
    
      # update the display, incase search terms were adjusted
      self.search_and_display()
      
    except:
      self.log.error("MainPanel::start_configs ex " + traceback.format_exc() ) 


          
  def cancel_downloads(self, event):
    """
    Cancel all downloads
    """
    
    self.log.info("MainPanel::cancel_downloads start")
    try:
      
      # clear download Q.
      try:
        while True:
          self.requestQ.get_nowait()
      except Queue.Empty, e:
        pass
        
      for item in self.item_list:
        self.log.debug("checking item status " + item.title + " : " + str( item.status ) )
        
        if item.status == AudioDetails.STATUS_START_DOWNLOAD or item.status == AudioDetails.STATUS_DOWNLOADING:
          self.issue_command( COMMAND_CANCEL_DOWNLOAD, item )
          
    except:
      self.log.error("MainPanel::cancel_downloads ex " + traceback.format_exc() ) 


  def start_download(self, event):
    """
    Start the downloads based on selection
    """


    self.log.info("MainPanel::start_download start")
    try:
      
      # always tries to make sure threads are running.
      # probably a bad way to do it.
      self.create_download_threads()
      
      for item in self.item_list:

        if item.checked_for_download:      
          
          # check if we're not already downloading it.
          if item.status !=  AudioDetails.STATUS_START_DOWNLOAD:
      
            request = {}
            request['COMMAND'] = COMMAND_START_DOWNLOAD
            request['ITEM'] = item
            
            self.log.debug("Putting " + str( item ) + " on download queue")
            
            #if item.status != AudioDetails.STATUS_populateList:
            self.requestQ.put( request )
        
    except:
      self.log.error("MainPanel::start_download ex " + traceback.format_exc() ) 

              
  def update_list_button(self, event):
    """
    Starts the link downloader to download a new set of links.
    This is threaded and ran in the background.
    Once the list is complete self.threaded_link_downloader.have_new_results will be True
    and the timer can detect this and get the results for processing.
    """
    
    self.log.info("MainPanel::update_list_button start")
    try:
      
      self.threaded_link_downloader = ThreadedLinkDownloader( self.config )
      self.threaded_link_downloader.start()
      
      #self.threaded_link_downloader.generate_latest_links()

      dlg = wx.ProgressDialog("Updating contents from BBC",
                               "More info required?",
                               maximum = 100,
                               parent = self,
                               style = wx.PD_CAN_ABORT
                                | wx.PD_APP_MODAL
                                | wx.PD_ELAPSED_TIME
                                | wx.PD_REMAINING_TIME
                                )

      keepGoing = True
      count = 0

      while keepGoing and count < 100:
          count = self.threaded_link_downloader.get_percent_complete()
          
          (keepGoing, skip) = dlg.Update(count)

              
      dlg.Destroy()

      
    except:
      self.log.error("MainPanel::update_list_button ex " + traceback.format_exc() ) 
 
